%skeleton "lalr1.cc"
%require "2.4.1"
%defines
%define parser_class_name "Parser"

%code requires {
/*
 * TODO language 
 * Copyright (c) 2010 TODO Team
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <vector>
#include "scanner.h"
#include "types.h"
#include "ast.h"

#define YYSTYPE todo::ast::ExprAST*

namespace todo {
class Driver;
}
}

// The parsing context.
%parse-param { todo::Driver& driver }
%parse-param { ScannerState* state }
%lex-param   { todo::Driver& driver }
%lex-param   { ScannerState* state }
     
%locations
%initial-action {
// Initialize the initial location.
@$.begin.filename = @$.end.filename = &driver.file;
};

%debug
%error-verbose

%code {
#include "driver.h"

std::vector<todo::ast::ExprAST*> nodes;
}

%token END  0      "end of file"
%token EXIT        "exit"
%token BW_AND_EQUAL "&="
%token BW_AND       "&"
%token BW_OR_EQUAL "|="
%token BW_OR       "|"
%token BW_XOR      "^"
%token EQUAL       "=="
%token NOT_EQUAL   "!="
%token ASSIGN      "="
%token XOR_EQUAL   "^="
%token MINUS_EQUAL "-="
%token PLUS_EQUAL  "+="
%token MULT_EQUAL  "*="
%token DIV_EQUAL   "/="
%token MOD_EQUAL   "%="
%token SHIFT_RIGHT ">>"
%token SHIFT_LEFT  "<<"
%token SHIFT_LEFT_EQUAL "<<="
%token SHIFT_RIGHT_EQUAL ">>="
%token LESS        "<"
%token GREATER     ">"
%token LESS_EQUAL  "<="
%token GREATER_EQUAL ">="
%token MOD_SEPARATOR "::"
%token MODULE      "module"
%token IF          "if"
%token ELSE        "else"
%token ELSIF       "elsif"
%token LOOP        "loop"
%token IDENT 	   "identifier"
%token RETURN      "return"
%token BREAK       "break"
%token PRINTLN     "println"
%token PRINT       "print"
%token DEF         "def"
%token CASE        "case"
%token WHEN        "when"
%token OTHERS      "others"
%token STR         "chars"
%token VAR         "var"
%token INTEGER     "int"
%token BOOL        "bool"
%token STRING      "string"
%token DOUBLE      "double"
%token MAP         "map"
%token VECTOR      "vector"
%token TUPLE 	   "tuple"
%token OBJECT      "object"
%token NUM_INTEGER "num_integer"
%token NUM_DOUBLE  "num_double"
%token TRUE        "true"
%token FALSE       "false"
%token INCREMENT   "inc"
%token DECREMENT   "dec"
%token IMPORT      "import"
%token ISEP        "newline"
%token EOS			"eos"

%left ',';
%left BW_OR BW_AND BW_XOR;
%left ASSIGN;
%left ':';
%left '|';
%left '^';
%left '&';
%nonassoc EQUAL NOT_EQUAL;
%nonassoc LESS LESS_EQUAL GREATER GREATER_EQUAL;
%left PLUS_EQUAL MINUS_EQUAL DIV_EQUAL MULT_EQUAL MOD_EQUAL BW_OR_EQUAL XOR_EQUAL BW_AND_EQUAL;
%left SHIFT_LEFT SHIFT_RIGHT;
%left '-' '+' '.';
%left '*' '/' '%';
%right '!';
%right '~' INCREMENT DECREMENT;
%right '[' '{';
%left ELSIF;
%left ELSE;
%left UMINUS;

%%

%start top_statements;

top_statements: statement_list { 
	std::vector<todo::ast::ExprAST*>::iterator it = nodes.begin();
	
	while (it < nodes.end()) {
		std::cout << (*it)->debug() << std::endl;
		delete *it;
		++it;
	} 
} ;

statement_list:		statement_list statements 
	|	/* empty */
;

statement_list2:		statement_list2 statements_2
	|	/* empty */
;

statements:		module_stmt
	|	statements_2
	|	def_stmt
	|	import_stmt
;

statements_2:	return_stmt
	|	exit_stmt
	|	if_stmt
	|	print_stmt
	|	break_stmt
	|	loop_stmt
	|	var_stmt
	|	assign_stmt
	|	case_stmt
	|	func_call
	|	method_call
	|	static_method_call
	|	var_expr
	|	end
;

end:	"newline"
	|	"eos"
;

import_stmt:	"import" "chars" end
;

slice_and_subscript_deref: '[' expr ':' expr ']' 
	|	'[' expr ']' 
	|	'[' ':' expr ']'
	|	'[' expr ':' ']'
;

slice_and_subscript_expr:	object '[' expr ':' expr ']'
	|	object '[' expr ']'
	|	object '[' ':' expr ']'
	|	object '[' expr ':' ']'
;

variable: "identifier" "::" "identifier"
	|	"identifier"
;

var_expr:	"inc" variable
	|	"dec" variable
	|	variable "inc"
	|	variable "dec"
;

map_indexing: "identifier" '.' "identifier"
;

expr:	expr '-' expr { nodes.push_back(new todo::ast::BinaryExprAST('-', $1, $3)); }
	|	expr '+' expr { nodes.push_back(new todo::ast::BinaryExprAST('+', $1, $3)); }
	|	expr '/' expr { nodes.push_back(new todo::ast::BinaryExprAST('/', $1, $3)); }
	|	expr '*' expr { nodes.push_back(new todo::ast::BinaryExprAST('*', $1, $3)); }
	|	expr '%' expr { nodes.push_back(new todo::ast::BinaryExprAST('%', $1, $3)); }
	|	expr ">=" expr
	|	expr "<=" expr
	|	expr "<" expr
	|	expr ">" expr
	|	expr "<<" expr
	|	expr ">>" expr
	|	expr "|" expr
	|	expr "^" expr
	|	expr "&" expr
	|	expr "==" expr
	|	expr "!=" expr
	|	'-' expr %prec UMINUS
	|	'+' expr %prec UMINUS
	|	'!' expr
	|	'~' expr
	|	var_expr
	|	method_call
	|	static_method_call
	|	object
	|	func_call
	|	slice_and_subscript_expr
	|	'(' expr ')'
	|	map_indexing
;

map_expr_list: map_expr_list ',' "identifier" ':' expr
	|	"identifier" ':' expr
;

vector_expr_list: vector_expr_list ',' expr
	|	expr
;

object:	variable
	|	"num_integer" { $$ = $1; }
	|	"num_double"
	|	"chars"
	|	"true"
	|	"false"
	|	'[' vector_expr_list ']'
	|	'[' ']'
	|	'{' map_expr_list '}'
	|	'{' '}'
;

object_expr:	slice_and_subscript_expr
	|	object
	|	'(' expr ')'
	|	func_call
;

call_list_args:	call_list_args ',' expr
	|	expr
;

method_chaining: method_chaining '.' "identifier" '(' ')'
	|	method_chaining '.' "identifier" '(' call_list_args ')' 
	|	method_chaining slice_and_subscript_deref 
	|	/* empty */
;

static_method_call:	type "::" "identifier" '(' ')' method_chaining
	|	type "::" "identifier" '(' call_list_args ')' method_chaining
;

method_call:	object_expr '.' "identifier" '(' ')' method_chaining
	|	object_expr '.' "identifier" '(' call_list_args ')' method_chaining
;

func_deref:	func_deref slice_and_subscript_deref
	| 	/* empty */
;

func_call:	"identifier" '(' call_list_args ')' func_deref
	|	"identifier" '(' ')' 
		func_deref
;

assign_stmt:	"identifier" "=" expr end 
	|	"identifier" "-=" expr end 
	|	"identifier" "+=" expr end 
	|	"identifier" "/=" expr end 
	|	"identifier" "*=" expr end 
	|	"identifier" "%=" expr end 
	|	"identifier" "^=" expr end 
	|	"identifier" "<<=" expr end 
	|	"identifier" ">>=" expr end 
	|	"identifier" "|=" expr end 
	|	"identifier" "&=" expr end 
;

opt_elsif_stmt:		opt_elsif_stmt "elsif" '-' expr ':'	statement_list
	|	/* empty */
;

opt_else_stmt:		"else" ':' statement_list
	|	/* empty */
;

if_stmt:	"if" '-' expr ':' statement_list
		opt_elsif_stmt opt_else_stmt ';'
;

return_stmt:	"return" expr end
	|	"return" end
;

exit_stmt:	"exit" end
	|	"exit" "num_integer" end

print_list_expr: print_list_expr ',' expr
	|	expr
;

println_list_expr: println_list_expr ',' expr
	|	expr
;

print_stmt:		"print" print_list_expr end
	|	"println" println_list_expr end
;

type: 	"int"
	|	"double"
	|	"bool"
	|	"string"
	|	"tuple"  "<" type tuple_args ">"
	|	"vector" "<" type ">"
	|	"vector"
	|	"map"    "<" type ">"
	|	"map"
	|	"object"
;

tuple_args: /* empty */
	|	',' type
;

list_var_stmt:	list_var_stmt ',' "identifier" "=" expr
	|	list_var_stmt ',' "identifier"
	|	/* empty */
;

var_stmt:	type "identifier" "=" expr list_var_stmt end
	|	type "identifier" list_var_stmt end
;

loop_var: type "identifier" "=" expr list_var_stmt
	|	type "identifier" list_var_stmt
	|	/* empty */
;

loop_stmt:	"loop" '-' expr ':'
		statement_list ';'
	|	"loop" '-' loop_var ';' expr ';' expr ':' statement_list ';'
;

case_when_list:	case_when_list "when" expr ':' statement_list ';'
	|	"when" expr ':'
;

case_stmt:	"case" expr ':' case_when_list "others" ':' statement_list ';' ';'
	|	"case" expr ':' case_when_list ';'
;

break_stmt:		"break" end
	|	"break" "num_integer" end
;

def_list_args:	def_list_args ',' type "identifier"
	|	type "identifier"
;

def_stmt:	"def" type "identifier" ':'
				statement_list2 ';'
	|	"def" type "identifier" '-' def_list_args ':' statement_list2 ';'
	|	"def" "identifier" ':' statement_list2 ';'
	|	"def" "identifier" '-' def_list_args ':' statement_list2 ';'
;

module_stmt:	"module" "identifier" ':' statement_list ';'
;

%%

void yy::Parser::error(const yy::Parser::location_type& line, const std::string& message)
{
	driver.error(line, message);
}
